package animator.phantom.plugin;

/*
    Copyright Janne Liljeblad

    This file is part of Phantom2D.

    Phantom2D is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Phantom2D is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with Phantom2D.  If not, see <http://www.gnu.org/licenses/>.
*/
import animator.phantom.renderer.param.AnimCoords3D;
import animator.phantom.renderer.*;
import animator.phantom.renderer.threedee.*;
import animator.phantom.paramedit.CoordsEditComponents3D;
import animator.phantom.controller.ThreeDeeController;
import com.sun.j3d.utils.image.TextureLoader;
import javax.media.j3d.*;
import java.awt.image.BufferedImage;
import java.awt.*;
import javax.vecmath.*;
import giotto2D.filters.merge.ImageToAlpha;

/**
* Base class for plugins with 3D functionality.
*/
public abstract class Phantom3DPlugin extends PhantomPlugin
{
	private static Phantom3DUniverse universe = null;
	private Appearance app = null;

	public void init3DPlugin()
	{
		initPlugin( THREE_DEE );
		// We use the same universe for all 3D objects.
		if( universe == null )
			universe = new Phantom3DUniverse();
	}
	/**
	* Registers AnimatedImageCoordinates object to control animated affine transforms.
	* <code>AnimatedImageCoordinates</code> has number of parameter parameters (x,y, XScale,...) that are placed in proper inner data structures. 
	* @throws PhantomPluginInitError If called twice or called on plugin that is not of type FREE_SOURCE, FULL_SCREEN_MOVING_SOURCE.
	*/
	protected void register3DCoords()
	{
		if( getIOP().getCoords() != null )
		{
			PhantomPluginInitError e = new PhantomPluginInitError( "Coords registered twice." );
			throw e;
		}	
		getIOP().registerCoords( new AnimCoords3D( getIOP() ) );
	}

	/**
	* Adds editors for parameters created by calling <code>registerCoords()</code>.
	*/
	public void add3DCoordsEditors()
	{
		if( getIOP().getCoords() == null )
		{
			PhantomPluginInitError e = new PhantomPluginInitError( "Added editors for non existing 3D coords" );
			throw e;
		}
		CoordsEditComponents3D coords = new CoordsEditComponents3D( getIOP() );
		getEditPanel().addComponentsVector( coords.getEditComponents() );
	}

	/**
	* Returns components defining 3D transformation used to render 
	*/
	public AnimCoords3D get3DCoords()
	{
		return (AnimCoords3D) getIOP().getCoords();
	}
	/**
	* Renders image of shape in 3D using user selected current camera and lightscene for plugin instance.
	*/
	public BufferedImage render3DImage( float frameTime, AnimCoords3D coords, Node shape, Dimension screenSize )
	{
		Plugin3DSourceIOP iop = (Plugin3DSourceIOP) getIOP();
		Phantom3DCamera camera = ThreeDeeController.getCamera( iop.cameraID.get() );
		//Phantom3DLightScene lightScene = ThreeDeeController.getLightScene( iop.lightSceneIndex.get() );
		return universe.renderImage( frameTime, coords, camera, shape, screenSize );
	}

	/**
	* Saves a textured appearance to be reused later. Convenience method.
	*/
	protected void setTexturedAppearance( BufferedImage bImage )
	{
		app = createTexturedAppearance( bImage );
	}
	/**
	* Saves a appearance to be reused later. Convenience method.
	*/
	protected void setAppearance( Appearance newApp )
	{
		app = newApp;
	}
	/**
	* Returns a saved a appearance to be reused. Convenience method.
	*/
	protected Appearance getAppearance()
	{
		return app;
	}
	protected static Appearance createTexturedAppearance( BufferedImage bImage )
	{
		// Create textured appearance object
		Appearance newApp = new Appearance();

		int flags = TextureLoader.BY_REFERENCE | TextureLoader.Y_UP;
		flags |= TextureLoader.ALLOW_NON_POWER_OF_TWO;
		flags |= TextureLoader.GENERATE_MIPMAP;

		Texture tex = new TextureLoader( bImage, flags ).getTexture();
		tex.setMagFilter(Texture.BASE_LEVEL_LINEAR);
		tex.setMinFilter(Texture.MULTI_LEVEL_LINEAR);

		newApp.setTexture( tex );
		TextureAttributes texAttr = new TextureAttributes();
		texAttr.setTextureMode(TextureAttributes.MODULATE);
		newApp.setTextureAttributes( texAttr );

		return newApp;
	}

	public static Appearance getWhiteAppearance()
	{
		return getColorAppearance( Color.white );
	}

	public static Appearance getColorAppearance( Color c )
	{
		Appearance newApp = new Appearance();
		Color3f col = new Color3f ( (float) c.getRed() / 255.0f, (float) c.getGreen() / 255.0f,(float) c.getBlue() / 255.0f );
		ColoringAttributes colorAttr = new ColoringAttributes( col, 1 );
		newApp.setColoringAttributes( colorAttr );
		return newApp;
	}

	public static QuadArray getTexturePolygon( BufferedImage img )
	{
		// --- Create geometry points. Make longer size length 1.0f.
		float h, w;
		int height = img.getHeight();
		int width = img.getWidth();
		if(  height > width )
		{
			h = 1.0f;
			w = ((float) width) / ((float) height);
		}
		else
		{
			h = ((float) height) / ((float) width);
			w = 1.0f;
		}
		Point3f[] pts = new Point3f[4];
		pts[1] = new Point3f(w / 2.0f, -h / 2.0f, 0.0f);
		pts[2] = new Point3f(w / 2.0f, h / 2.0f, 0.0f);
		pts[3] = new Point3f(-w / 2.0f, h / 2.0f, 0.0f);
		pts[0] = new Point3f(-w / 2.0f, -h / 2.0f, 0.0f);

		//--- Create textured geometry array
		int[] texCoordSetMap = new int[1];
		texCoordSetMap[0] = 0;
		QuadArray polygon = new QuadArray(  4, 
						    QuadArray.COORDINATES | GeometryArray.TEXTURE_COORDINATE_2,
						    1,
 						    texCoordSetMap);
		polygon.setCoordinate( 0, pts[0] );
		polygon.setCoordinate( 1, pts[1] );
		polygon.setCoordinate( 2, pts[2] );
		polygon.setCoordinate( 3, pts[3] );

		polygon.setTextureCoordinate( 0, 0, new TexCoord2f(0.0f,0.0f) );
		polygon.setTextureCoordinate( 0, 1, new TexCoord2f(1.0f,0.0f) ); 
		polygon.setTextureCoordinate( 0, 2, new TexCoord2f(1.0f,1.0f) );
		polygon.setTextureCoordinate( 0, 3, new TexCoord2f(0.0f,1.0f) );

		return polygon;
	}

	/**
	* Sets BW image as destination images alpha channel.
	*/
	protected static void combineAlpha( BufferedImage dest, BufferedImage BWAlpha )
	{
		ImageToAlpha.filterFull( dest, BWAlpha );
	}

}//end class
